package com.study.mike.common.config;

import com.study.mike.common.constants.RedisKey;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.interceptor.CacheErrorHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.time.Duration;
import java.util.HashMap;
import java.util.Map;


/**
 * 自定义Redis缓存管理器
 * <p>主要用于@Cacheable缓存的有效期</p>
 *
 * @author zhouhp
 * @date 2022/4/13
 */
@Configuration
@Slf4j
public class RedisConfig extends CachingConfigurerSupport {

    /**
     * 实例化 RedisTemplate 对象
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory factory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        //设置Redis键值对的序列化方式
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        redisTemplate.setConnectionFactory(factory);
        return redisTemplate;
    }

    /**
     * 指定缓存空间的有效期
     */
    private Map<String, RedisCacheConfiguration> getCustomConfig() {
        Map<String, RedisCacheConfiguration> configMap = new HashMap<>(4);

        //特定缓存空间、及其有效期
        configMap.put(RedisKey.HALF_HOUR, this.getCustomConfig(Duration.ofMinutes(30)));
        configMap.put(RedisKey.ONE_HOUR, this.getCustomConfig(Duration.ofHours(1)));
        configMap.put(RedisKey.SIX_HOUR, this.getCustomConfig(Duration.ofHours(6)));
        configMap.put(RedisKey.ONE_DAY, this.getCustomConfig(Duration.ofDays(1)));
        return configMap;
    }

    /**
     * 自定义缓存管理器
     */
    @Bean
    public CacheManager cacheManager(LettuceConnectionFactory lettuceConnectionFactory) {
        RedisCacheConfiguration defaultConfig = RedisCacheConfiguration.defaultCacheConfig()
                //默认所有值的有效期为2小时
                .entryTtl(Duration.ofHours(2))
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));

        return RedisCacheManager
                .builder(RedisCacheWriter.nonLockingRedisCacheWriter(lettuceConnectionFactory))
                //自定义缓存空间
                .withInitialCacheConfigurations(this.getCustomConfig())
                //默认缓存空间
                .cacheDefaults(defaultConfig)
                .build();
    }


    private RedisCacheConfiguration getCustomConfig(Duration ttl) {
        return RedisCacheConfiguration.defaultCacheConfig()
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()))
                .entryTtl(ttl);
    }

    /**
     * 缓存异常处理
     */
    @Bean
    @Override
    public CacheErrorHandler errorHandler() {
        return new CacheErrorHandler() {
            @Override
            public void handleCacheGetError(RuntimeException e, Cache cache, Object key) {
                log.error("handleCacheGetError异常key{}", key);
            }

            @Override
            public void handleCachePutError(RuntimeException e, Cache cache, Object key, Object value) {
                log.error("handleCachePutError异常key{}", key);
            }

            @Override
            public void handleCacheEvictError(RuntimeException e, Cache cache, Object key) {
                log.error("handleCacheEvictError异常key{}", key);
            }

            @Override
            public void handleCacheClearError(RuntimeException e, Cache cache) {
                log.error("缓存异常CacheClearError");
            }
        };
    }
}
